/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package lintulaskenta.validointi;

import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import lintulaskenta.utils.Document.Node;
import lintulaskenta.entity.*;
import lintulaskenta.utils.HttpClientService;
import lintulaskenta.utils.TipuAPIClient;
import lintulaskenta.utils.TipuAPIClientInterface;
import lintulaskenta.utils.TipuApiResource;
import lintulaskenta.validointi.validointiKirjasto;
import org.apache.http.client.methods.HttpPost;

/**
 *
 * @author Pajunen
 */
public class TipuApiValidator {

    private String kunta;
    public TipuAPIClient api = null;
    public TipuApiResource lintulajit = null;
    public TipuApiResource kunnat = null;
    public TipuApiResource havainnoijat = null;
    public TipuApiResource koodit = null;
    public HttpClientService kokoAPI = null;
    //tipun yhteyttä varten muuttujia
    private String[] salasanat;
    private BufferedReader buffreader;
    public validointiKirjasto validointikirjasto;
    private FileWriter loggaaja;
    private File virheilmoitukset;

    public TipuApiValidator() throws FileNotFoundException, IOException {
        pohjustaResurssit();
    }

    /*
     * Luodaan yhteys tipuAPIin ja noudetaan tarvittavat resurssit (lintulajit etc)
     * Lopuksi yhteys suljetaan
     */
    /*
     * Luokaa omaan kansioon tiedosto, jossa ensimmäisellä rivillä url, tokalla käyttäjätunnus ja kolmannella passu.
     * Ei sitten vahingossa mene googleen tipun passuja, vaan korkeintaan teidän kotihakemiston polku.
     * Jotain ongelmia ilmennyt laitoksenkin koneiden kanssa että tiedoston luku failaa ja tulee /HR-tagin puuttumisesta
     * virheilmoa. Jotain häikkää tässä tapauksessa salasanafilun lukemisessa. Merkistön tulisi olla UTF-8 ja rivinvaihdomerkin
     * UNIX.
     */
    private void salasanat() throws FileNotFoundException, IOException {
        salasanat = new String[3];
        FileInputStream file = new FileInputStream("/Users/jukkapajunen/Documents/Koulu/OhTuProje/tipu-apin_passut.txt");
        DataInputStream in = new DataInputStream(file);
        buffreader = new BufferedReader(new InputStreamReader(in));
        salasanat[0] = buffreader.readLine().trim();
        salasanat[1] = buffreader.readLine().trim();
        salasanat[2] = buffreader.readLine().trim();
    }

    private void pohjustaResurssit() throws FileNotFoundException, IOException {
        virheilmoitukset = new File("Virheilmoitukset");
        loggaaja = new FileWriter(virheilmoitukset);
        salasanat();
        try {
            api = new TipuAPIClient(salasanat[0], salasanat[1], salasanat[2]); // korjaa salasanat yms.
            kunnat = api.get(TipuAPIClientInterface.MUNICIPALITIES);
            lintulajit = api.get(TipuAPIClientInterface.SPECIES);
            koodit = api.get(TipuAPIClientInterface.CODES);
            havainnoijat = api.get(TipuAPIClientInterface.RINGERS);
            kokoAPI = new HttpClientService(salasanat[0], salasanat[1], salasanat[2]);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (api != null) {
                api.close();
            }
        }
    }

    public HttpClientService getKokoAPI() {
        return kokoAPI;
    }

    public TipuApiResource getKoodit() {
        return koodit;
    }

    public void sulje() {
        kokoAPI.close();
    }


    /*
     * Palauttaa true, jos tipuAPI:sta löytyy kuusimerkkisellä lintukoodilla matchi
     * else false
     */
    public boolean tarkistaLintulaji(String lintu) {
        Node haettavaLintu = lintulajit.getById(lintu);
        if (haettavaLintu != null && lintu.length() == 6) {
            //System.out.println(haettavaLintu);
            return true;
        }
        return false;
    }

    /*
     * Maksimiarvo parven koolle 999
     */
    private boolean tarkistaParvenKoko(int parvenKoko) {
        if (parvenKoko < 1000 && parvenKoko > 1) {
            return true;
        } else {
            return false;
        }
    }

    /*
     * Palauttaa true, jos tipuAPI:sta löytyy MAKSIMISSAAN kuusimerkkisellä kuntakoodilla matchi
     * else false
     */
    public boolean tarkistaKunta(String kunta) {
        Node haettavaKunta = kunnat.getById(kunta);
        if (haettavaKunta != null && kunta.length() <= 6) {
            //System.out.println(haettavaKunta);
            return true;
        } else {
            return false;
        }
    }

    public boolean tarkistaHavainnoijanumero(int numero) {
        if (numero > 0 && numero < 10000) {
            //System.out.println(haettavaKunta);
            return true;
        } else {
            return false;
        }
    }

    public boolean tarkistaRivinJuoksevanumero(int numero) throws IOException {
        if (numero > 0 && numero < 33) {         
            return true;
        } else {
            System.out.println("Rivin juokesevanumero (" + numero + ") ei ole validi.");
            loggaaja.append("Rivin juokesevanumero (" + numero + ") ei ole validi.\n");
        }
        return false;
    }

    public boolean tarkistaPisteidenLkm(int pisteidenLkm) throws IOException {
        if (pisteidenLkm == 20) {                      // Mietittävä vielä pullauttimen puolella
            //System.out.println(ReitinNumerot);
            return true;
        } else {
            System.out.println("Huom! Reitillä on " + pisteidenLkm + " pistettä!!");
            loggaaja.append("Huom! Reitillä on " + pisteidenLkm + " pistettä!!\n");
        }
        return false;
    }
    /*
     * Aika turha testi tällaisenaan
     */

    public boolean tarkistaLahempiPaikka(String lahempiPaikka) {
        if (lahempiPaikka.length() > 0 && lahempiPaikka.length() < 18) {  // tarkistaLahempiPaikka joka on max. 17 kirjainta
            //System.out.println(ReitinNumerot);
            return true;
        } else {
            return false;
        }
    }

    /*
     * Annetaan parametreinä perättäisdatasta kunnan lyhenne, latitudeksi sarakkeet 15-17(NNN) ja longitudeksi sarakkeet 18 & 19
     * Toistaiseksi täytyy ottaa tietokantayhteys jokaisen haun yhteydessä, luultavasti aika raskas operaatio.
     */
    public boolean tarkistapeninkulmaruutu(String kunta, int lati, int longi) throws IOException {
        HttpPost request = new HttpPost("https://h92.it.helsinki.fi/tipu-api/coordinate-validation-service?municipality=" + kunta.trim() + "&lat=" + lati + "3000&lon=" + longi + "3000&type=UNIFORM");
        String tuloste = kokoAPI.contentAsString(request);
        if (tuloste.contains("true")) {
            return true;
        } else {
            System.out.println(" Kunta ja koordinaatit eivät matchaa: " + kunta + ":  P:" + lati + "  I:" + longi + "\n" + tuloste);
        }
        loggaaja.append(" Kunta ja koordinaatit eivät matchaa: " + kunta + ":  P:" + lati + "  I:" + longi + "\n" + tuloste + "\n");

        return false;
    }
    /*
     * Tarkistetaan reittioliolta kaikki mahdollinen:
     * reittinumero, lähempi paikka, laskenta-ajat ja päivämäärät, havainnoijanumero, kunta, kunnan ja annetujen koordinaattien matchaus,
     * pisteiden lukumäärä ja kutsutaan vielä erikseen tarkistaPistevuodet-metodia
     */
    public boolean tarkistaReittiOlio(Reitti tarkistettava) throws IOException, Exception {
        long reitinNumero = -333;

        boolean eiOleVirheita = true; // jos kaikki testit oikein, palautetaan true
        boolean onkoKuntaOikein = true;
        String ekaVuosi = null;
        String ekaAika = null;
        for (Havainnointitapahtuma h : tarkistettava.getHavainnointitapahtumat()) { // Vaan FileWriteria varten, jotta saadaan kirjattua reitin laskentavuosi vain alkuun
            if (reitinNumero == -333) {
                reitinNumero = tarkistettava.getVanhaReittiNumero();
                loggaaja.append("\nReitti: " + reitinNumero + "\n");
                ekaVuosi = h.getPaiva() + "." + h.getKuukausi() + "." + h.getVuosi();
                ekaAika = h.getAlkuTunnit() + ":" + h.getAlkuMinuutit();
            }
            kunta = h.getLaskentakunta().trim();

            if (tarkistaLahempiPaikka(h.getLahempiPaikka()) == false) {
                eiOleVirheita = false;
                System.out.println("Lähempipaikka sisältää liikaa tai liian vähän merkkejä: " + h.getLahempiPaikka() + ", vuosi: " + h.getVuosi());
                loggaaja.append("(" + reitinNumero + "" + h.getVuosi() + ") Lähempipaikka sisältää liikaa tai liian vähän merkkejä: " + h.getLahempiPaikka() + ", vuosi: " + h.getVuosi() + "\n");
                eiOleVirheita = false;
            }
            if (validointikirjasto.kaykoLaskentaAika(tarkistettava, h) == false) {
                System.out.println("Reitin laskenta-ajoissa on heittoa: \nTunnit: " + h.getAlkuTunnit() + "\nminuutit: " + h.getAlkuMinuutit() + ", vuosi: " + h.getVuosi());
                loggaaja.append("(" + reitinNumero + "" + h.getVuosi() + ") Reitin laskenta-ajoissa on heittoa. Pitäisi olla +-30min: " + ekaAika + ", tämän reitin aika: " + h.getAlkuTunnit() + ":" + h.getAlkuMinuutit() + "\n");
                eiOleVirheita = false;
            }
            if (validointikirjasto.kaykoLaskentaPvm(tarkistettava, h) == false) {
                System.out.println("Reitin laskentapäivämäärissä on heittoa: \nVuosi: " + h.getVuosi() + "\nKuukausi: " + h.getKuukausi() + "\nPäivä: " + h.getPaiva());
                loggaaja.append("(" + reitinNumero + "" + h.getVuosi() + ") Reitin laskentapäivämäärissä on heitto. Pitäisi olla +-7 päivää: " + ekaVuosi + ", tämän reitin aika: " + h.getPaiva() + "." + h.getKuukausi() + "." + h.getVuosi() + "\n");
                eiOleVirheita = false;
            }
            if (h.getAlkuTunnit() >= h.getLoppuTunnit()) {
                if (h.getAlkuTunnit() == h.getLoppuTunnit() && h.getAlkuMinuutit() > h.getLoppuMinuutit()) {
                    System.out.println("Reitin laskenta on alkanut aiemmin kuin päättynyt!");
                    loggaaja.append("Reitin laskenta on alkanut aiemmin kuin päättynyt!");
                }
            }
            if (tarkistaHavainnoijanumero(h.getHavainnoijaNumero()) == false) {
                System.out.println("Havainnoijanumero: " + h.getHavainnoijaNumero() + " on virheellinen.");
                loggaaja.append("(" + reitinNumero + "" + h.getVuosi() + ") Havainnoijanumero: " + h.getHavainnoijaNumero() + " on virheellinen." + "\n");
                eiOleVirheita = false;
            }
            if (tarkistaKunta(kunta) == false) {
                System.out.println(kunta + " ei ole validi kunnan lyhenne vuonna: " + h.getVuosi());
                loggaaja.append("(" + reitinNumero + "" + h.getVuosi() + ") " + kunta + " ei ole validi kunnan lyhenne vuonna: " + h.getVuosi() + "\n");
                eiOleVirheita = false;
                onkoKuntaOikein = false;
            }
            if (onkoKuntaOikein == true && tarkistapeninkulmaruutu(kunta,
                    tarkistettava.getPohjoiskoordinaatti(), tarkistettava.getItakoordinaatti()) == false) {
                loggaaja.append("(" + reitinNumero + "" + h.getVuosi() + ") Vuonna: " + h.getVuosi());
                System.out.println("Kunta ja koordinaatit eivät matchaa!");
                eiOleVirheita = false;
            }
        }
        if (tarkistaPistevuodenTapahtumat(tarkistettava) == false) {
            eiOleVirheita = false;
        }
        if (tarkistaPisteidenLkm(tarkistettava.getPisteidenLkm()) == false) {
            loggaaja.append("Pisteitä " + tarkistettava.getPisteidenLkm() + " kpl!");

            eiOleVirheita = false;
        }

        if (eiOleVirheita == true) {
            loggaaja.append("Reitillä ei virheitä.\n");
        }
        return eiOleVirheita;
    }
    /*
     * Tarkistetaan onko lintulajin lyhenne yhteneväinen tipuapin kanssa ja onko parvenkoko järkevän kokoinen.
     */

    private boolean tarkistaPistevuodenTapahtumat(Reitti tarkistettava) throws IOException {
        boolean eiOleVirheita = true;
        for (Havaintopiste h : tarkistettava.getHavaintopisteet()) {
            tarkistaPistevuosi(h);
            for (Pistevuosi p : h.getPistevuodet()) {

                for (Parvihavainto l : p.getParvihavainnot()) {
                    if (tarkistaLintulaji(l.getParvilaji().trim()) == false) {
                        System.out.println("Parven laji on virheellinen Piste: " + p.getPisteenJarjestysnumero() + " Parven lyhenne: " + l.getParvilaji());
                        loggaaja.append("(" + tarkistettava.getVanhaReittiNumero() + "" + p.getVuosi() + ") Parven laji on virheellinen. Piste: " + p.getPisteenJarjestysnumero() + " Parven lyhenne: " + l.getParvilaji() + "\n");
                        eiOleVirheita = false;
                    }
                    if (tarkistaParvenKoko(l.getParvenKoko()) == false) {
                        System.out.println("Parven koko on virheellinen. Piste: " + p.getPisteenJarjestysnumero() + ", reitti: " + p.getReitinNumero() + ", parven koko: " + l.getParvenKoko() + " (" + l.getParvilaji() + ")");
                        loggaaja.append("(" + tarkistettava.getVanhaReittiNumero() + "" + p.getVuosi() + ") Parven koko on virheellinen. Piste: " + p.getPisteenJarjestysnumero() + ", reitti: " + p.getReitinNumero() + ", parven koko: " + l.getParvenKoko() + " (" + l.getParvilaji() + ")\n");
                        eiOleVirheita = false;
                    }
                }
            }
        }
        return eiOleVirheita;
    }

    private boolean tarkistaBiotooppiEnnenJaNyt(int bioEnnen, int bioNyt) {
        if (bioEnnen != bioNyt) {
            return false;
        }
        return true;
    }
    /*
     * Tarkistaa pistevuodelta säteen, biotoopin ja synkkaako edellisen vuoden biotooppi nykyisen biotoopin kanssa.
     */

    private boolean tarkistaPistevuosi(Havaintopiste tarkistettava) throws IOException {
        boolean eiOleVirheita = true;
        int vanhaBiotooppi = -1919191;
        int vanhaVuosi = -33;
        for (Pistevuosi p : tarkistettava.getPistevuodet()) {
            if (tarkistaSade(p.getReitinSade()) == false) {
                System.out.println("Reitin säde on " + p.getReitinSade());
                loggaaja.append("(" + p.getReitinNumero() + "" + p.getVuosi() + ") Vuonna: " + p.getVuosi() + " reitin säde on " + p.getReitinSade() + "\n");
                eiOleVirheita = false;
            }
            if (tarkistaBiotooppi(p.getBiotooppikoodiNyt()) == false) {
                System.out.println("Vuonna: " + p.getVuosi() + " biotooppikoodi on virheellinen. Piste: " + p.getPisteenJarjestysnumero() + "\nReitin numero: " + p.getReitinNumero() + ", biotooppi: " + p.getBiotooppikoodiNyt());
                loggaaja.append("(" + p.getReitinNumero() + "" + p.getVuosi() + ") Vuonna: " + p.getVuosi() + " biotooppikoodi on virheellinen. Piste: " + p.getPisteenJarjestysnumero() + "Reitin numero: " + p.getReitinNumero() + ", biotooppi: " + p.getBiotooppikoodiNyt() + "\n");
                eiOleVirheita = false;
            }
            if (vanhaBiotooppi != -1919191 && p.getBiotooppikoodiEnnen() != 0 && tarkistaBiotooppiEnnenJaNyt(p.getBiotooppikoodiEnnen(), vanhaBiotooppi) == false) {
                System.out.println("Biotooppikoodi ennen(" + p.getBiotooppikoodiEnnen() + ") ei ole asetettu oikein. Pitäisi olla: " + vanhaBiotooppi + ". Piste: " + p.getPisteenJarjestysnumero() + ", vuonna: " + p.getVuosi() + "\n");
                loggaaja.append("(" + p.getReitinNumero() + "" + p.getVuosi() + ") Biotooppikoodi ennen(" + p.getBiotooppikoodiEnnen() + ") ei ole asetettu oikein. Pitäisi olla: " + vanhaBiotooppi + ". Piste: " + p.getPisteenJarjestysnumero() + ", vuonna: " + p.getVuosi() + "\n");
                eiOleVirheita = false;

            }
            /* Otetaan talteen pistevuoden tapahtumat, jotta voidaan verrata seuraavana vuonna niiden vaihtuvuutta*/
            vanhaBiotooppi = p.getBiotooppikoodiNyt();
            vanhaVuosi = p.getVuosi();
        }
        return eiOleVirheita;
    }

    private boolean tarkistaSade(int sade) {
        if (sade == 50 || sade == 25) {
            return true;
        }
        return false;
    }

    private boolean tarkistaBiotooppi(int tarkistettava) {
        if (tarkistettava < 1 || tarkistettava > 17) {
            return false;
        }
        return true;
    }

    public void suljeWriter() throws IOException {
        loggaaja.close();
    }
}




